package com.kalua.list.dbaccess.product;

import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
import static com.datastax.driver.core.querybuilder.QueryBuilder.in;
import static com.datastax.driver.core.querybuilder.QueryBuilder.set;
import static com.kalua.list.dbaccess.product.ProductRowMapper.ALL_PRODUCT_COLUMNS;
import static com.kalua.list.dbaccess.product.ProductRowMapper.BATCH_CREATED_COL_NAME;
import static com.kalua.list.dbaccess.product.ProductRowMapper.BATCH_LAST_UPDATED_COL_NAME;
import static com.kalua.list.dbaccess.product.ProductRowMapper.CLASSIFICATIONS_COL_NAME;
import static com.kalua.list.dbaccess.product.ProductRowMapper.FULL_IMAGE_URL_COL_NAME;
import static com.kalua.list.dbaccess.product.ProductRowMapper.PRICE_COL_NAME;
import static com.kalua.list.dbaccess.product.ProductRowMapper.PRODUCT_COL_FAMILY;
import static com.kalua.list.dbaccess.product.ProductRowMapper.PRODUCT_ID_COL_NAME;
import static com.kalua.list.dbaccess.product.ProductRowMapper.PRODUCT_NAME_COL_NAME;
import static com.kalua.list.dbaccess.product.ProductRowMapper.RETAILER_ISSUED_ID_COL_NAME;
import static com.kalua.list.dbaccess.product.ProductRowMapper.THUMBNAIL_IMAGE_URL_COL_NAME;
import static com.kalua.list.dbaccess.retailer.RetailerRowMapper.RETAILER_ID_COL_NAME;

import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.springframework.cassandra.core.RowMapper;
import org.springframework.data.cassandra.core.CassandraTemplate;

import com.datastax.driver.core.Statement;
import com.datastax.driver.core.querybuilder.Insert;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select.Where;
import com.google.common.collect.Sets;
import com.kalua.list.dbaccess.AbstractCRUDDAO;
import com.kalua.list.domainmodel.NumericId;
import com.kalua.list.domainmodel.TextIdentifier;
import com.kalua.list.domainmodel.product.Product;

public class ProductDAOImpl 	extends AbstractCRUDDAO<Product, NumericId> 
												implements ProductDAO {

	public ProductDAOImpl(CassandraTemplate template) 
	{super(template);}

	@Override
	public Set<Product> findByRetailerId(	TextIdentifier retailerIssuedId,
																TextIdentifier retailerId) {
		Set<Product> result;
		
		Where statement = 
				QueryBuilder.select(ALL_PRODUCT_COLUMNS)
									.from(PRODUCT_COL_FAMILY)
									.allowFiltering()
									.where(eq(RETAILER_ID_COL_NAME, retailerId.getValue()))
									.and(eq(RETAILER_ISSUED_ID_COL_NAME, retailerIssuedId.getValue()));
		
		
		Map<NumericId, Product> queryResults = executeMultiRowRead(statement);

		result = Sets.newHashSet(queryResults.values());
		
		return result;
	}
	
	@Override
	public Map<NumericId, Product> findByRetailerId(TextIdentifier retailerId) {
		Map<NumericId, Product> results;
		Where statement = 
				QueryBuilder.select(ALL_PRODUCT_COLUMNS)
									.from(PRODUCT_COL_FAMILY)
									.allowFiltering()
									.where(eq(RETAILER_ID_COL_NAME, retailerId.getValue()));
		results = executeMultiRowRead(statement);		
		return results;
	}
	

	@Override
	protected Statement buildInsertStatement(Product entity) {
		Insert insert =  
				QueryBuilder	.insertInto(PRODUCT_COL_FAMILY)
										.value(PRODUCT_ID_COL_NAME, entity.getId().getValue())
			        					.value(RETAILER_ID_COL_NAME, entity.getRetailerId().getValue())
			        					.value(RETAILER_ISSUED_ID_COL_NAME, entity.getRetailerIssuedId().getValue())
			        					.value(PRODUCT_NAME_COL_NAME, entity.getName())
			        					.value(PRICE_COL_NAME, entity.getPrice())
			        					.value(FULL_IMAGE_URL_COL_NAME, entity.getFullImageURL())
			        					.value(THUMBNAIL_IMAGE_URL_COL_NAME, entity.getThumbnailImageURL())
			        					.value(CLASSIFICATIONS_COL_NAME, entity.getClassifications())
			        					.value(BATCH_CREATED_COL_NAME, resolveToDatabaseArg(entity.getBatchCreated()))
			        					.value(BATCH_LAST_UPDATED_COL_NAME, resolveToDatabaseArg(entity.getBatchLastUpdated()))
										.ifNotExists();
		return insert;
	}

	@Override
	protected Statement buildMultiRowSelect(Object[] keys) {
		Statement statement = QueryBuilder.select().all()
				.from(PRODUCT_COL_FAMILY)
				.where(in(PRODUCT_ID_COL_NAME, keys));
		return statement;
	}

	@Override
	protected Statement buildReadStatement(NumericId id) {
		Statement statement = QueryBuilder.select().all()
				.from(PRODUCT_COL_FAMILY)
				.where(eq(PRODUCT_ID_COL_NAME, id.getValue()));
		return statement;	
	}

	@Override
	protected Statement buildReadAllStatement() {
		Statement statement = 
				QueryBuilder	.select().all()
										.from(PRODUCT_COL_FAMILY);
		return statement;
	}

	@Override
	protected Statement buildUpdateStatement(Product entity) {
		Statement update =  
				QueryBuilder	.update(PRODUCT_COL_FAMILY)
										.with(set(RETAILER_ID_COL_NAME, entity.getRetailerId().getValue()))
										.and(set(RETAILER_ISSUED_ID_COL_NAME, entity.getRetailerIssuedId().getValue()))
			        					.and(set(PRODUCT_NAME_COL_NAME, entity.getName()))
			        					.and(set(PRICE_COL_NAME, entity.getPrice()))
			        					.and(set(FULL_IMAGE_URL_COL_NAME, entity.getFullImageURL()))
			        					.and(set(THUMBNAIL_IMAGE_URL_COL_NAME, entity.getThumbnailImageURL()))
			        					.and(set(CLASSIFICATIONS_COL_NAME, entity.getClassifications()))
			        					.and(set(BATCH_CREATED_COL_NAME, resolveToDatabaseArg(entity.getBatchCreated())))
			        					.and(set(BATCH_LAST_UPDATED_COL_NAME, resolveToDatabaseArg(entity.getBatchLastUpdated())))
			        					.where(eq(PRODUCT_ID_COL_NAME, entity.getId().getValue()));
		return update;
	}

	@Override
	protected Statement buildDeleteStatement(NumericId id) {
		Statement statement = 
				QueryBuilder.delete()
		        		.from(PRODUCT_COL_FAMILY)
		        		.where(eq(PRODUCT_ID_COL_NAME, id.getValue()));
		return statement;
	}

	@Override
	protected Class<NumericId> getIdType() { return NumericId.class; }
	
	@Override
	protected RowMapper<Product> getRowMapper() 
	{ return new ProductRowMapper(); }

	@Override
	protected Object[] toObjectArray(Collection<NumericId> keys) 
	{ return keys.stream().map(k -> k.getValue()).collect(Collectors.toList()).toArray(); }
}